#ifndef _QEXTSERIALBASE_H_
#define _QEXTSERIALBASE_H_

#include <QIODevice>
#include <QFile>
#include <QThread>
#include <QMutex>

/*if all warning messages are turned off, flag portability warnings to be turned off as well*/
#ifdef _TTY_NOWARN_
#define _TTY_NOWARN_PORT_
#endif

/*macros for thread support*/
#define LOCK_MUTEX() mutex->lock()
#define UNLOCK_MUTEX() mutex->unlock()

/*macros for warning and debug messages*/
#ifdef _TTY_NOWARN_PORT_
#define TTY_PORTABILITY_WARNING(s)
#else
#define TTY_PORTABILITY_WARNING(s) qWarning(s)
#endif /*_TTY_NOWARN_PORT_*/
#ifdef _TTY_NOWARN_
#define TTY_WARNING(s)
#else
#define TTY_WARNING(s) qWarning(s)
#endif /*_TTY_NOWARN_*/


/*line status constants*/
#define LS_CTS  0x01
#define LS_DSR  0x02
#define LS_DCD  0x04
#define LS_RI   0x08
#define LS_RTS  0x10
#define LS_DTR  0x20
#define LS_ST   0x40
#define LS_SR   0x80

/*error constants*/
#define E_NO_ERROR                   0
#define E_INVALID_FD                 1
#define E_NO_MEMORY                  2
#define E_CAUGHT_NON_BLOCKED_SIGNAL  3
#define E_PORT_TIMEOUT               4
#define E_INVALID_DEVICE             5
#define E_BREAK_CONDITION            6
#define E_FRAMING_ERROR              7
#define E_IO_ERROR                   8
#define E_BUFFER_OVERRUN             9
#define E_RECEIVE_OVERFLOW          10
#define E_RECEIVE_PARITY_ERROR      11
#define E_TRANSMIT_OVERFLOW         12
#define E_READ_FAILED               13
#define E_WRITE_FAILED              14

/*!
 * Enums for port settings.
 */
enum NamingConvention
{
    WIN_NAMES,
    IRIX_NAMES,
    HPUX_NAMES,
    SUN_NAMES,
    DIGITAL_NAMES,
    FREEBSD_NAMES,
    LINUX_NAMES
};

enum BaudRateType
{
    BAUD50,                //POSIX ONLY
    BAUD75,                //POSIX ONLY
    BAUD110,
    BAUD134,               //POSIX ONLY
    BAUD150,               //POSIX ONLY
    BAUD200,               //POSIX ONLY
    BAUD300,
    BAUD600,
    BAUD1200,
    BAUD1800,              //POSIX ONLY
    BAUD2400,
    BAUD4800,
    BAUD9600,
    BAUD14400,             //WINDOWS ONLY
    BAUD19200,
    BAUD38400,
    BAUD56000,             //WINDOWS ONLY
    BAUD57600,
    BAUD76800,             //POSIX ONLY
    BAUD115200,
    BAUD128000,            //WINDOWS ONLY
    BAUD256000             //WINDOWS ONLY
};

enum DataBitsType
{
    DATA_5,
    DATA_6,
    DATA_7,
    DATA_8
};

enum ParityType
{
    PAR_NONE,
    PAR_ODD,
    PAR_EVEN,
    PAR_MARK,               //WINDOWS ONLY
    PAR_SPACE
};

enum StopBitsType
{
    STOP_1,
    STOP_1_5,               //WINDOWS ONLY
    STOP_2
};

enum FlowType
{
    FLOW_OFF,
    FLOW_HARDWARE,
    FLOW_XONXOFF
};

/**
 * structure to contain port settings
 */
struct PortSettings
{
    BaudRateType BaudRate;//波特率设置
    DataBitsType DataBits;//数据位设置
    ParityType Parity;//奇偶校验设置
    StopBitsType StopBits;//停止位设置
    FlowType FlowControl;//数据流控制设置
    long Timeout_Millisec;//延时设置
};

/*!
 * \author Stefan Sander
 * \author Michal Policht
 *
 * A common base class for Win_QextSerialBase, Posix_QextSerialBase and QextSerialPort.
 */
class QextSerialBase : public QIODevice
{
    Q_OBJECT

    public:
        enum QueryMode {
            Polling,
            EventDriven
        };

    protected:
        QMutex* mutex;
        QString port;
        PortSettings Settings;
        ulong lastErr;
        QextSerialBase::QueryMode _queryMode;//查询模式

        virtual qint64 readData(char * data, qint64 maxSize)=0;
        virtual qint64 writeData(const char * data, qint64 maxSize)=0;

    public:
        QextSerialBase();
        QextSerialBase(const QString & name);
        virtual ~QextSerialBase();
        virtual void construct();
        virtual void setPortName(const QString & name);
        virtual QString portName() const;

        /**!
         * Get query mode.
         * 	\return query mode.
         */
        inline QextSerialBase::QueryMode queryMode() const { return _queryMode; };

        /*!
         * Set desired serial communication handling style. You may choose from polling
         * or event driven approach. This function does nothing when port is open; to
         * apply changes port must be reopened.
         *
         * In event driven approach read() and write() functions are acting
         * asynchronously. They return immediately and the operation is performed in
         * the background, so they doesn't freeze the calling thread.
         * To determine when operation is finished, QextSerialPort runs separate thread
         * and monitors serial port events. Whenever the event occurs, adequate signal
         * is emitted.
         *
         * When polling is set, read() and write() are acting synchronously. Signals are
         * not working in this mode and some functions may not be available. The advantage
         * of polling is that it generates less overhead due to lack of signals emissions
         * and it doesn't start separate thread to monitor events.
         *
         * Generally event driven approach is more capable and friendly, although some
         * applications may need as low overhead as possible and then polling comes.
         *
         * \param mode query mode.
         */
        virtual void setQueryMode(QueryMode mode);

//		virtual void setBlockingRead(bool block) = 0; ///< @todo implement.

        virtual void setBaudRate(BaudRateType)=0;
        virtual BaudRateType baudRate() const;
        virtual void setDataBits(DataBitsType)=0;
        virtual DataBitsType dataBits() const;
        virtual void setParity(ParityType)=0;
        virtual ParityType parity() const;
        virtual void setStopBits(StopBitsType)=0;
        virtual StopBitsType stopBits() const;
        virtual void setFlowControl(FlowType)=0;
        virtual FlowType flowControl() const;
        virtual void setTimeout(long)=0;

        virtual bool open(OpenMode mode)=0;
        virtual bool isSequential() const;
        virtual void close()=0;
        virtual void flush()=0;

        virtual qint64 size() const=0;
        virtual qint64 bytesAvailable()=0;
        virtual bool atEnd() const;

        virtual void ungetChar(char c)=0;
        virtual qint64 readLine(char * data, qint64 maxSize);

        virtual ulong lastError() const;
        virtual void translateError(ulong error)=0;

        virtual void setDtr(bool set=true)=0;
        virtual void setRts(bool set=true)=0;
        virtual ulong lineStatus()=0;

    signals:
        /**
         * This signal is emitted whenever port settings are updated.
         * 	\param valid \p true if settings are valid, \p false otherwise.
         *
         * 	@todo implement.
         */
//		void validSettings(bool valid);

        /*!
         * This signal is emitted whenever dsr line has changed its state. You may
         * use this signal to check if device is connected.
         * 	\param status \p true when DSR signal is on, \p false otherwise.
         *
         * 	\see lineStatus().
         */
        void dsrChanged(bool status);
};

#endif
